; ==============================================================================
; Apple II [$D0 ROM] (341-0016) - Programmer's Aid #1 [1978]
; ------------------------------------------------------------------------------
; Part 5 [$D5B0~$D691]: RAM Test Routine by Steve Wozniak [WOZ], 1977-06;
; Copyright (c) 1978 by Apple Computer Inc. All Rights Reserved
; ------------------------------------------------------------------------------
; Instructions are in the Programmer's Aid #1 Installation and Operating Manual
; ==============================================================================
; Analyzed (via McFadden's SourceGen) by James Davis [Last Updated: 2020-07-10]
; ==============================================================================
;
; ==============================================================================
; RAM Test Routine 6502 Equates:
; ==============================================================================
;
DATA EQU $00 {addr/1} ;[Normal] Test DATA ($00|$FF)
NDATA EQU $01 {addr/1} ;Inverted Test NDATA ($FF|$00)
TESTD EQU $02 {addr/1} ;Gallop TEST Data (see below)
R3L EQU $06 {addr/1} ;Sweet 16 Register #3, Low
R3H EQU $07 {addr/1} ;Sweet 16 Register #3, High
R4L EQU $08 {addr/1} ;Sweet 16 Register #4, Low
R4H EQU $09 {addr/1} ;Sweet 16 Register #4, High
R5L EQU $0A {addr/1} ;Sweet 16 Register #5, Low
R5H EQU $0B {addr/1} ;Sweet 16 Register #5, High
R6L EQU $0C {addr/1} ;Sweet 16 Register #6, Low
R6H EQU $0D {addr/1} ;Sweet 16 Register #6, High
R7L EQU $0E {addr/1} ;Sweet 16 Register #7, Low
R7H EQU $0F {addr/1} ;Sweet 16 Register #7, High
A1H EQU $3D {addr/1} ;Monitor General Purpose A1-Reg, High
A2L EQU $3E {addr/1} ;Monitor General Purpose A2-Reg, Low
INBUFF EQU $0200 ;Input Buffer (6502 Page 2)
USRADR EQU $03F8 {addr/3} ;Monitor User Command (Ctrl-Y) Vector
PRBYTE EQU $FDDA ;Print A-Reg as Two-Digit Hex Number
COUT EQU $FDED ;Print A-Reg to Output Device
PRERR EQU $FF2D ;Print "ERR" & Sound Bell (Beep)
BELL EQU $FF3A ;Monitor Sound Bell Subroutine
ORG $D5B0
;
; ==============================================================================
; Monitor User Command (Ctrl-Y) Vector Setup Routine:
; ==============================================================================
; Shared by: RAM Test, Relocater, & Tape Verify Routines
; ------------------------------------------------------------------------------
; *v* (Not in "RAMTEST" listing in the "Installation and Operating Manual"!) *v*
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;Setup Mon User Cmd (Ctrl-Y) Vector:
D5B0: 8D F9 03 SETUSRADR STA USRADR+1 ;Preset User Address, Low
D5B3: 8C FA 03 STY USRADR+2 ;Preset User Address, High
D5B6: A9 4C LDA #$4C ;Get JMP OpCode
D5B8: 8D F8 03 STA USRADR ;Preset to JMP OpCode
D5BB: 60 RTS ;Return to Caller
; ==============================================================================
; RAM Test Routines:
; ==============================================================================
;
; ------------------------------------------------------------------------------
; Set Monitor User Command (Ctrl-Y) Vector to RAM Test Routine Location:
; ------------------------------------------------------------------------------
;
; *** Typing "D5BCG <Return>" at Monitor Prompt (*) init's the RAM Test Program
;
; ----------------------------------- ;Setup Mon User Cmd (Ctrl-Y) Location:
D5BC: A9 C3 SETRTCYV LDA #<RAMTEST ;Get RAM Test Routine Address, Low
D5BE: A0 D5 LDY #>RAMTEST ;Get RAM Test Routine Address, High
D5C0: 4C B0 D5 JMP SETUSRADR ;Setup Mon User Cmd (Ctrl-Y) Vector
; ==============================================================================
; RAM Test Routine:
; ==============================================================================
;
; *** Typing "Adrs.Pgs Ctrl-Y <Return>" at the Monitor Prompt (*) tests RAM,
; starting at the hexadecimal address (Adrs) given, and continuing through the
; number of hexadecimal pages (Pgs) given. The starting address (Adrs) low byte
; must be typed in, but its value (<> $00) is ignored in this routine, so RAM
; Tests always start & end at a 6502 page boundary. Pgs must be <= Adrs page.
; See additional instructions in the "Programmer's Aid #1 Installation and
; Operating Manual" (Ch.6 RAM Test, Pages 29~34).
;
; ----------------------------------- ;Main (Ctrl-Y) Entry Point:
D5C3: A9 00 RAMTEST LDA #0 ;Set [Normal] Test DATA = Zero
D5C5: 20 D0 D5 JSR RAMTEST0 ;Test RAM with [Normal] Test DATA
D5C8: A9 FF LDA #$FF ;Set [Normal] Test DATA = ($FF|255|-1)
D5CA: 20 D0 D5 JSR RAMTEST0 ;Test RAM with [Normal] Test DATA
D5CD: 4C 3A FF JMP BELL ;Sound Bell (Beep); Returns to Caller
; ----------------------------------- ;Initialize DATA & Inverse NDATA Safes:
D5D0: 85 00 RAMTEST0 STA DATA ;Save Test DATA ($00|$FF)
D5D2: 49 FF EOR #%11111111 ;Invert Test DATA ($00|$FF)->($FF|$00)
D5D4: 85 01 STA NDATA ;Save Inverted Test NDATA ($FF|$00)
; ----------------------------------- ;Initialize Pointers:
D5D6: A5 3D LDA A1H ;Get Start of Test Block Address, High
D5D8: 85 07 STA R3H ;Init Pointer: Aux-Register #3, High
D5DA: 85 09 STA R4H ;Init Pointer: Aux-Register #4, High
D5DC: 85 0B STA R5H ;Init Pointer: Aux-Register #5, High
D5DE: A0 00 LDY #0 ;Get Start of Test Block Address, Low
; ;^[A1L is ignored, so RAM Tests always ]
; ; [start & end at a 6502 Page Boundary!]
D5E0: 84 06 STY R3L ;Init Pointer: Aux-Register #3, Low
D5E2: 84 08 STY R4L ;Init Pointer: Aux-Register #4, Low
D5E4: 84 0A STY R5L ;Init Pointer: Aux-Register #5, Low
D5E6: A6 3E LDX A2L ;Get Length (Pages) [from Monitor]
; ----------------------------------- ;Set Entire Test Block (Bytes)=(DATA):
D5E8: A5 00 LDA DATA ;Retrieve Test DATA ($00|$FF)
D5EA: 91 08 RAMTEST1 STA (R4L),Y ;Store (DATA) in each Byte of Test Block
D5EC: C8 INY ;Advance Byte Pointer [0..255]
D5ED: D0 FB BNE RAMTEST1 ;Loop until 256 Memory Bytes have been Set
D5EF: E6 09 INC R4H ;Advance Page Pointer
D5F1: CA DEX ;Reduce Page Counter (Length)
D5F2: D0 F6 BNE RAMTEST1 ;Loop until all Pages are Done
; ----------------------------------- ;Verify Entire Test Block (Bytes)=(DATA):
D5F4: A6 3E LDX A2L ;Get Length (Pages) [from Monitor]
D5F6: B1 06 RAMTEST2 LDA (R3L),Y ;Retrieve Memory Byte from Test Block
D5F8: C5 00 CMP DATA ;Assure it is Correct [(Memory) = (DATA)]
D5FA: F0 13 BEQ RAMTEST3 ;Branch if it is Correct [(A) = (DATA)]
; ----------------------------------- ;Else, Memory is Incorrect; Handle Error;
; ;Print Bad Memory's Adrs & Bit-Error Data:
D5FC: 48 PHA ;Preserve Bad (Memory Failure) Data
; ;Print Memory Address:
D5FD: A5 07 LDA R3H ;Get Start of Test Block Address, High
D5FF: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number
D602: 98 TYA ;Get Byte Pointer
D603: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
; ;Print Expected Data & Bad Memory Data:
D606: A5 00 LDA DATA ;Retrieve Test DATA ($00|$FF)
D608: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D60B: 68 PLA ;Retrieve Bad (Memory Failure) Data
; ------------------------------------------------------------------------------
D60C: 20 92 D6 JSR RTBADMEM ;Print Bad Memory Chip's Location: The ...
; ;Row & Column on Apple II Motherboards
; ------------------------------------------------------------------------------
; The line above is code from an actual PA1 ROM. It is changed from that in
; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing
; (pages 84~86). [$D60C:2092D6 JSR $D692 versus 207FD6 JSR $D67F, respectively]
; ------------------------------------------------------------------------------
; [versus] JSR RTPRBYCR ;Print (A):[##], Error + Beep, Space + CR;
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;Continue Test Block Verification:
D60F: C8 RAMTEST3 INY ;Advance Byte Pointer [0..255]
D610: D0 E4 BNE RAMTEST2 ;Loop until 256 Bytes have been Verified
D612: E6 07 INC R3H ;Advance Byte Pointer [0..255]
D614: CA DEX ;Reduce Page Counter (Length)
D615: D0 DF BNE RAMTEST2 ;Loop until all Pages are Done
; ----------------------------------- ;Set Entire Test Block (Bytes)=(NDATA) &
; ;Gallop Through Memory Bytes [Test Loops]:
; ;^[Testing Memory Test Bytes for Changes ]
; ;^[when Neighboring Address Bytes are Set]
D617: A6 3E LDX A2L ;Get Length (Pages) [from Monitor]
D619: A5 01 RAMTEST4 LDA NDATA ;Get Inverted Test NDATA ($FF|$00)
D61B: 91 0A STA (R5L),Y ;Store (NDATA) in each Byte of Test Block
; ------------------------- ;Setup Gallop Bit Mask (Aux-Register #6):
D61D: 84 0D STY R6H ;Set Bit Mask, High = Byte Ptr [0..255]
D61F: 84 0C STY R6L ;Set Bit Mask, Low = Byte Ptr [0..255]
D621: E6 0C INC R6L ;Advance Gallop Bit Mask, Low [1..256|0]
; ----------------------------------- ;Gallop Through Memory Bytes [Test Loop]:
D623: A5 01 RAMTEST5 LDA NDATA ;Retrieve Inverted Test NDATA ($FF|$00)
D625: 20 45 D6 JSR RAMTEST6 ;Gallop with Inverted Test NDATA
D628: A5 00 LDA DATA ;Retrieve [Normal] Test DATA ($00|$FF)
D62A: 20 45 D6 JSR RAMTEST6 ;Gallop with [Normal] Test DATA
; ------------------------- ;Shift Gallop Bit Mask (Aux-Register #6)
; ;to Test Next Neighbor Bit:
D62D: 06 0C ASL R6L ;Shift Gallop Test Bit Mask, Low
D62F: 26 0D ROL R6H ;Shift Gallop Test Bit Mask, High
D631: A5 0D LDA R6H ;Retrieve Gallop Test Bit Mask, High
D633: C5 3E CMP A2L ;Is Bit-Mask > Length? (Pages)
D635: 90 EC BCC RAMTEST5 ;NO, Loop if Not Done
D637: A5 00 LDA DATA ;YES, Done; Retrieve Test DATA ($00|$FF)
D639: 91 0A STA (R5L),Y ;Restore Memory Test Byte
D63B: E6 0A INC R5L ;Advance Memory Byte Pointer
D63D: D0 DA BNE RAMTEST4 ;Loop until 256 Bytes have been Tested
D63F: E6 0B INC R5H ;Advance Memory Page Pointer
D641: CA DEX ;Reduce Page Counter (Length)
D642: D0 D5 BNE RAMTEST4 ;Loop until all Pages are Done
D644: 60 RT_RTS1 RTS ;Return to Caller
; ----------------------------------- ;Gallop Through Memory Bytes [Test S/R]:
D645: 85 02 RAMTEST6 STA TESTD ;Save Gallop TEST Data
; ------------------------- ;Set Pointer (Aux-Register #4)
; ;for Neighbor Address (1-Bit Difference):
D647: A5 0A LDA R5L ;Get Memory Byte Pointer
D649: 45 0C EOR R6L ;Set (A)=(R5L XOR R6L)
D64B: 85 08 STA R4L ;Set Pointer (Aux-Register #4, Low)
D64D: A5 0B LDA R5H ;Get Memory Page Pointer
D64F: 45 0D EOR R6H ;Set (A)=(R5H XOR R6H)
D651: 85 09 STA R4H ;Set Pointer (Aux-Register #4, High)
; ------------------------- ;Test Memory Test Bytes for Changes
; ;when Neighboring Address Bytes are Set:
D653: A5 02 LDA TESTD ;Retrieve Gallop TEST Data
D655: 91 08 STA (R4L),Y ;Set Neighbor Memory Byte = TEST Data
D657: B1 0A LDA (R5L),Y ;Retrieve Memory Test Byte (Different Ptr)
D659: C5 01 CMP NDATA ;Check for a Change: is (A)=(NDATA)?
D65B: F0 E7 BEQ RT_RTS1 ;Return to Caller if OK: (A)=(NDATA)
; ----------------------------------- ;Else, Test Byte is Changed; Handle Error:
D65D: 48 PHA ;Preserve Bad (Memory Failure) Data
D65E: A5 0B LDA R5H ;Get Memory Test Page Pointer
D660: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number
D663: A5 0A LDA R5L ;Get Memory Test Byte Pointer
D665: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D668: A5 01 LDA NDATA ;Retrieve Inverted Test NDATA ($FF|$00)
D66A: 91 0A STA (R5L),Y ;Replace/Correct Bad Memory with NDATA
D66C: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D66F: 68 PLA ;Retrieve Bad (Memory Failure) Data
; ------------------------------------------------------------------------------
; ** This jump causes a crash into the Monitor unless there is code at $02CB! **
; ** There is info about this out in Cyberspace, but I have not found it yet! **
; ------------------------------------------------------------------------------
D670: 4C CB 02 JMP INBUFF+203 ;Go to User Routine near End of Input Page
; ------------------------------------------------------------------------------
; The line above is code from an actual PA1 ROM. It is changed from that in
; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing
; (pages 84~86). [$D670:4CCB02 JMP $02CB versus 208AD6 JSR $D68A, respectively]
; ------------------------------------------------------------------------------
; [versus] JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
; ------------------------------------------------------------------------------
; ** The following cannot happen unless the User Routine jumps back to $D673! **
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;Continue/Finish Handling Error:
D673: A5 09 RTERRFIN LDA R4H ;Get Neighbor Memory Test Page Pointer
D675: 20 DA FD JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number
D678: A5 08 LDA R4L ;Get Neighbor Memory Test Byte Pointer
D67A: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D67D: A5 02 LDA TESTD ;Retrieve Gallop TEST Data
;
; ------------------------------------------------------------------------------
; The following subroutine was used as such (see "[versus]" above at $D60C) in
; the "Programmer's Aid #1 Installation and Operating Manual" "RAMTEST" listing
; (pages 84~86). Now, in PA1 ROMs, it is not used as a separate subroutine.
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;Print (A):[##], Error + Beep, Space + CR:
D67F: 20 8A D6 RTPRBYCR JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D682: 20 2D FF JSR PRERR ;Print "ERR" & Sound Bell
;
; ------------------------------------------------------------------------------
; ** (End of what cannot happen unless the User Routine jumps back to $D673!) **
; ------------------------------------------------------------------------------
;
D685: A9 8D RTCROUT LDA #$8D ;Get a Carriage Return (Ctrl-M) Character
D687: 4C ED FD JMP COUT ;Print A-Reg; Returns to Caller
;
; ----------------------------------- ;Print (A):[as Two-Digit Hex #] & a Space
D68A: 20 DA FD RTPRBYSP JSR PRBYTE ;Print A-Reg as Two-Digit Hex Number
D68D: A9 A0 LDA #' ' | $80 ;Get a Blank|Space Character
D68F: 4C ED FD JMP COUT ;Print A-Reg; Returns to Caller
; ==============================================================================
; *v* (End of "RAMTEST" listing in the "Installation and Operating Manual"!) *v*
; ==============================================================================
; 03F8:4C C3 D5 USRADR JMP RAMTEST ;Entry from Monitor (Ctrl-Y) <-[Renamed]
; ------------------------------------------------------------------------------
;
;
; ==============================================================================
; Print Bad Memory Chip's Row & Column Location on Apple II Moterboards:
; ==============================================================================
; *v* (Not in "RAMTEST" listing in the "Installation and Operating Manual"!) *v*
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;Flag Bad Bits in Bad Memory Byte:
D692: 84 0F RTBADMEM STY R7H ;Save Byte Pointer [0..255]
D694: 85 0E STA R7L ;Save Bad (Memory Failure) Data
D696: 20 8A D6 JSR RTPRBYSP ;Print (A):[as Two-Digit Hex #] & a Space
D699: 20 2D FF JSR PRERR ;Print "ERR" & Sound Bell
D69C: A5 00 LDA DATA ;Get [Normal] Test DATA ($00|$FF)
D69E: 45 0E EOR R7L ;Compute [(Test DATA) XOR (Bad DATA)]
D6A0: 85 0E STA R7L ;Save XOR Result = Bad Bits in Memory Byte
; ----------------------------------- ;Determine Chip's Motherboard Location:
D6A2: A0 07 LDY #7 ;Init Loop Counter (for 8 Iterations)
D6A4: 46 0E RTLOCLOOP LSR R7L ;Is Current Least Significant Bit Set?
D6A6: 90 23 BCC RTLOCNEXT ;Branch if Not a Bad Bit in Memory Byte
; ----------------------------------- ;Else, there is a Bad Bit in Memory Byte;
; ;Print Chip's (Row/Column) Location:
D6A8: A9 A0 LDA #' ' | $80 ;Get a Blank/Space Character
D6AA: 20 ED FD JSR COUT ;Print A-Reg to Output Device
D6AD: A5 3D LDA A1H ;Get Start of Test Block Address, High
D6AF: C9 50 CMP #$50 ;Set Carry if [(A)>=(80)]
D6B1: A9 C4 LDA #'D' | $80 ;Get a 'D' Character
D6B3: 69 00 ADC #0 ;Add Carry from Comparison
D6B5: 20 ED FD JSR COUT ;Print Motherboard Row [(A)=('D'|'E')]
; ;^[How does this print 'C' (for Row 'C')?]
D6B8: A9 AD LDA #'-' | $80 ;Get a Dash Character
D6BA: 20 ED FD JSR COUT ;Print A-Reg to Output Device
D6BD: 98 TYA ;Get Loop Counter: (Y)=(7,6,5,4,3,2,1,0)=
; ;^[(Y)=(Index to get Motherboard Column)]
D6BE: D0 05 BNE RTLOCPRDN ;Skip prefixing '1' character when (Y>0)
D6C0: A9 B1 LDA #'1' | $80 ;Get a '1' char to print '10' when (Y=0)
D6C2: 20 ED FD JSR COUT ;Print A-Reg to Output Device
D6C5: B9 D3 D6 RTLOCPRDN LDA DECNUMS,Y ;Get Decimal Number (Motherboard Column)=
; ;^[(A)=(3,4,5,6,7,8,9,0):(0 is column 10)]
D6C8: 20 ED FD JSR COUT ;Print A-Reg to Output Device
D6CB: 88 RTLOCNEXT DEY ;Reduce Loop Counter
D6CC: 10 D6 BPL RTLOCLOOP ;Loop until [(Y)=(-1)]
D6CE: A4 0F LDY R7H ;Retrieve Byte Pointer [0..255]
D6D0: 4C 85 D6 JMP RTCROUT ;Print Carriage Return; Returns to Caller
; ------------------------------------------------------------------------------
; Decimal Numbers Table: For RAM Chip Columns on A2 Motherboards
; ------------------------------------------------------------------------------
; [RAM Chip Rows = (C|D|E), Columns = (3,4,5,6,7,8,9,10)]
;
; ----------------------------------- ;RAM Chip Columns:
D6D3: B0 DECNUMS DFB '0' | $80 ;RAM Chip Column 10
D6D4: B9 DFB '9' | $80 ;RAM Chip Column 9
D6D5: B8 DFB '8' | $80 ;RAM Chip Column 8
D6D6: B7 DFB '7' | $80 ;RAM Chip Column 7
D6D7: B6 DFB '6' | $80 ;RAM Chip Column 6
D6D8: B5 DFB '5' | $80 ;RAM Chip Column 5
D6D9: B4 DFB '4' | $80 ;RAM Chip Column 4
D6DA: B3 DFB '3' | $80 ;RAM Chip Column 3
D6DB: B2 DFB '2' | $80 ;Not a RAM Chip Column
D6DC: B1 DFB '1' | $80 ;Not a RAM Chip Column